package org.dromara.business.service;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Telegram API 速率限制器
 *
 * Telegram Bot API 限制：
 * - 每秒最多发送 30 条消息
 * - 每分钟最多发送 20 条消息给同一个聊天
 * - 群组消息：每分钟最多发送 20 条消息
 * - 私聊消息：每秒最多发送 1 条消息
 */
@Slf4j
@Component
public class TelegramRateLimiter {

    // 全局速率限制：每秒最多30条消息
    private static final int GLOBAL_RATE_LIMIT_PER_SECOND = 30;

    // 单个聊天速率限制：每分钟最多20条消息
    private static final int CHAT_RATE_LIMIT_PER_MINUTE = 20;

    // 私聊速率限制：每秒最多1条消息
    private static final int PRIVATE_CHAT_RATE_LIMIT_PER_SECOND = 1;

    // 全局计数器
    private final AtomicInteger globalSecondCounter = new AtomicInteger(0);
    private volatile LocalDateTime lastGlobalReset = LocalDateTime.now();

    // 每个聊天的计数器 - 分钟级别
    private final ConcurrentHashMap<String, ChatRateInfo> chatMinuteCounters = new ConcurrentHashMap<>();

    // 私聊计数器 - 秒级别
    private final ConcurrentHashMap<String, ChatRateInfo> privateChatSecondCounters = new ConcurrentHashMap<>();

    /**
     * 聊天速率信息
     */
    private static class ChatRateInfo {
        private final AtomicInteger counter = new AtomicInteger(0);
        @Getter
        private volatile LocalDateTime lastReset = LocalDateTime.now();

        public int getCount() {
            return counter.get();
        }

        public void incrementAndGet() {
            counter.incrementAndGet();
        }

        public void reset() {
            counter.set(0);
            lastReset = LocalDateTime.now();
        }

    }

    /**
     * 检查是否允许发送消息到指定聊天
     *
     * @param chatId 聊天ID
     * @param isPrivateChat 是否为私聊
     * @return 是否允许发送
     */
    public boolean allowRequest(String chatId, boolean isPrivateChat) {
        // 检查全局速率限制
        if (!checkGlobalRateLimit()) {
            log.debug("Global rate limit exceeded, current count: {}", globalSecondCounter.get());
            return false;
        }

        // 检查私聊速率限制
        if (isPrivateChat && !checkPrivateChatRateLimit(chatId)) {
            log.debug("Private chat rate limit exceeded for chat: {}", chatId);
            return false;
        }

        // 检查聊天级别速率限制
        if (!checkChatRateLimit(chatId)) {
            log.debug("Chat rate limit exceeded for chat: {}", chatId);
            return false;
        }

        return true;
    }

    /**
     * 获取建议的等待时间（毫秒）
     *
     * @param chatId 聊天ID
     * @param isPrivateChat 是否为私聊
     * @return 建议等待时间（毫秒）
     */
    public long getSuggestedWaitTime(String chatId, boolean isPrivateChat) {
        LocalDateTime now = LocalDateTime.now();
        long maxWaitTime = 0;

        // 检查全局限制等待时间
        Duration globalWait = Duration.between(lastGlobalReset, now);
        if (globalWait.toMillis() < 1000 && globalSecondCounter.get() >= GLOBAL_RATE_LIMIT_PER_SECOND) {
            maxWaitTime = Math.max(maxWaitTime, 1000 - globalWait.toMillis());
        }

        // 检查私聊限制等待时间
        if (isPrivateChat) {
            ChatRateInfo privateInfo = privateChatSecondCounters.get(chatId);
            if (privateInfo != null) {
                Duration privateWait = Duration.between(privateInfo.getLastReset(), now);
                if (privateWait.toMillis() < 1000 && privateInfo.getCount() >= PRIVATE_CHAT_RATE_LIMIT_PER_SECOND) {
                    maxWaitTime = Math.max(maxWaitTime, 1000 - privateWait.toMillis());
                }
            }
        }

        // 检查聊天限制等待时间
        ChatRateInfo chatInfo = chatMinuteCounters.get(chatId);
        if (chatInfo != null) {
            Duration chatWait = Duration.between(chatInfo.getLastReset(), now);
            if (chatWait.toMillis() < 60000 && chatInfo.getCount() >= CHAT_RATE_LIMIT_PER_MINUTE) {
                maxWaitTime = Math.max(maxWaitTime, 60000 - chatWait.toMillis());
            }
        }

        return maxWaitTime;
    }

    /**
     * 记录一次成功的请求
     *
     * @param chatId 聊天ID
     * @param isPrivateChat 是否为私聊
     */
    public void recordRequest(String chatId, boolean isPrivateChat) {
        // 记录全局计数
        resetGlobalCounterIfNeeded();
        globalSecondCounter.incrementAndGet();

        // 记录聊天级别计数
        ChatRateInfo chatInfo = chatMinuteCounters.computeIfAbsent(chatId, k -> new ChatRateInfo());
        resetChatCounterIfNeeded(chatInfo, Duration.ofMinutes(1));
        chatInfo.incrementAndGet();

        // 记录私聊计数
        if (isPrivateChat) {
            ChatRateInfo privateInfo = privateChatSecondCounters.computeIfAbsent(chatId, k -> new ChatRateInfo());
            resetChatCounterIfNeeded(privateInfo, Duration.ofSeconds(1));
            privateInfo.incrementAndGet();
        }

        log.debug("Recorded request for chat: {}, isPrivate: {}, global: {}, chat: {}",
                 chatId, isPrivateChat, globalSecondCounter.get(), chatInfo.getCount());
    }

    /**
     * 检查全局速率限制
     */
    private boolean checkGlobalRateLimit() {
        resetGlobalCounterIfNeeded();
        return globalSecondCounter.get() < GLOBAL_RATE_LIMIT_PER_SECOND;
    }

    /**
     * 检查聊天级别速率限制
     */
    private boolean checkChatRateLimit(String chatId) {
        ChatRateInfo chatInfo = chatMinuteCounters.computeIfAbsent(chatId, k -> new ChatRateInfo());
        resetChatCounterIfNeeded(chatInfo, Duration.ofMinutes(1));
        return chatInfo.getCount() < CHAT_RATE_LIMIT_PER_MINUTE;
    }

    /**
     * 检查私聊速率限制
     */
    private boolean checkPrivateChatRateLimit(String chatId) {
        ChatRateInfo privateInfo = privateChatSecondCounters.computeIfAbsent(chatId, k -> new ChatRateInfo());
        resetChatCounterIfNeeded(privateInfo, Duration.ofSeconds(1));
        return privateInfo.getCount() < PRIVATE_CHAT_RATE_LIMIT_PER_SECOND;
    }

    /**
     * 重置全局计数器（如果需要）
     */
    private void resetGlobalCounterIfNeeded() {
        LocalDateTime now = LocalDateTime.now();
        Duration duration = Duration.between(lastGlobalReset, now);
        if (duration.toMillis() >= 1000) {
            synchronized (this) {
                if (Duration.between(lastGlobalReset, now).toMillis() >= 1000) {
                    globalSecondCounter.set(0);
                    lastGlobalReset = now;
                }
            }
        }
    }

    /**
     * 重置聊天计数器（如果需要）
     */
    private void resetChatCounterIfNeeded(ChatRateInfo chatInfo, Duration resetInterval) {
        LocalDateTime now = LocalDateTime.now();
        Duration duration = Duration.between(chatInfo.getLastReset(), now);
        if (duration.compareTo(resetInterval) >= 0) {
            synchronized (chatInfo) {
                if (Duration.between(chatInfo.getLastReset(), now).compareTo(resetInterval) >= 0) {
                    chatInfo.reset();
                }
            }
        }
    }

    /**
     * 获取当前速率限制状态
     */
    public String getRateLimitStatus() {
        StringBuilder status = new StringBuilder();
        status.append("Telegram Rate Limit Status:\n");
        status.append("Global (per second): ").append(globalSecondCounter.get()).append("/").append(GLOBAL_RATE_LIMIT_PER_SECOND).append("\n");

        // 显示活跃聊天的状态
        chatMinuteCounters.entrySet().stream()
            .filter(entry -> entry.getValue().getCount() > 0)
            .forEach(entry -> {
                String chatId = entry.getKey();
                int count = entry.getValue().getCount();
                status.append("Chat ").append(chatId).append(" (per minute): ").append(count).append("/").append(CHAT_RATE_LIMIT_PER_MINUTE).append("\n");
            });

        return status.toString();
    }

    /**
     * 清理过期的计数器
     */
    public void cleanupExpiredCounters() {
        LocalDateTime now = LocalDateTime.now();

        // 清理分钟级计数器
        chatMinuteCounters.entrySet().removeIf(entry -> {
            Duration duration = Duration.between(entry.getValue().getLastReset(), now);
            return duration.toMinutes() > 5; // 保留5分钟内的记录
        });

        // 清理秒级计数器
        privateChatSecondCounters.entrySet().removeIf(entry -> {
            Duration duration = Duration.between(entry.getValue().getLastReset(), now);
            return duration.toMinutes() > 1; // 保留1分钟内的记录
        });

        log.debug("Cleaned up expired rate limit counters");
    }
}
